package cn.bitkit.tools.crypto;

import cn.bitkit.base.util.StringUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.Security;

/**
 * 	对称加密
 * @author changbo
 *
 */
@Slf4j
public class SM4 {

    public static final String KEY_ALGORITHM = "SM4";
    public static final String CIPHER_ALGORITHM = "SM4/ECB/PKCS5Padding";
    public static final String CIPHER_ALGORITHM_HEX = "SM4/ECB/NOPADDING";
    /** 128bit; sm4只支持128bit秘钥 */
    public static final int KEY_SIZE = 128;
    public static final String CHARACTER_SET = "UTF-8";

    static{
        if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
            Security.addProvider(new BouncyCastleProvider());
        }
    }

    public static String encrypt(String data, String key) {
        return encrypt(data, key, null);
    }

    public static String encrypt(String data, String key, String iv) {
        byte[] ivBytes = (iv != null) ? iv.getBytes() : null;
        byte[] dataByte = data.getBytes();
        byte[] resBytes = cipherByte(dataByte, key.getBytes(), ivBytes, CIPHER_ALGORITHM, Cipher.ENCRYPT_MODE);
        return resBytes == null ? null : StringUtil.encodeBase64(resBytes);
    }

    public static String encryptHex(String data, String key) {
        return cipherHex(data, key, null, CIPHER_ALGORITHM_HEX, Cipher.ENCRYPT_MODE);
    }

    public static String decrypt(String data, String key) {
        return decrypt(data, key, null);
    }

    public static String decrypt(String data, String key, String iv) {
        byte[] ivBytes = (iv != null) ? iv.getBytes() : null;
        byte[] dataByte = StringUtil.decodeBase64(data);
        byte[] resBytes = cipherByte(dataByte, key.getBytes(), ivBytes, CIPHER_ALGORITHM, Cipher.DECRYPT_MODE);
        return resBytes == null ? null : new String(resBytes);
    }

    public static String decryptHex(String data, String key) {
        return cipherHex(data, key, null, CIPHER_ALGORITHM_HEX, Cipher.DECRYPT_MODE);
    }

    public static String cipherHex(String data, String key, String iv, String cipherAlgorithm, int mode) {
        byte[] ivBytes = (iv != null) ? StringUtil.hexStrToBytes(iv) : null;
        byte[] resBytes = cipherByte(StringUtil.hexStrToBytes(data), StringUtil.hexStrToBytes(key), ivBytes, cipherAlgorithm, mode);
        return resBytes == null ? null : StringUtil.bytesToHexStr(resBytes);
    }

    @SneakyThrows
    public static byte[] cipherByte(byte[] dataBytes, byte[] keyBytes, byte[] ivBytes, String cipherAlgorithm, int mode) {
        SecretKeySpec keySpec = SecretKey.paddingByteKey(keyBytes, KEY_ALGORITHM);
        Cipher cipher = Cipher.getInstance(cipherAlgorithm, BouncyCastleProvider.PROVIDER_NAME);
        if(ivBytes == null){
            cipher.init(mode, keySpec);
        }else{
            IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
            cipher.init(mode, keySpec, ivSpec);
        }
        return cipher.doFinal(dataBytes);
    }


}
